#include "../include/video.h"
#include "../include/defs.h"
#include "../include/kernel.h"

extern TTY tty[TTY_CANT];
extern promptlen;
extern int CurrentTTY;

static void showChar(unsigned short b);
static void breakLine(void);
static void backSpace(void);
static void tabSpace(void);
static void backLane(void);
unsigned short whiteSpace(void);

/**
 * Settea byte.
 */
int
videoSetByte(int b)
{
    int indexTTY = GetTTY();

    switch(b)
    {
        case '\n':
                    breakLine();
                    break;
        case '\b':
                    backSpace();
                    break;
        case '\t':
                    tabSpace();
                    break;
        case '\r':
                    backLane();
                    break;
        default:
                    showChar((unsigned char)b);
     }

    if (tty[indexTTY].screen.offset >=
        tty[indexTTY].screen.width * tty[indexTTY].screen.heigth)
    {
        scroll(1);

        /* Posiciona el offset en la ultima posicion valida para
        escritura de la pantalla y luego retraer el carril
         * al incio de la linea */

        tty[indexTTY].screen.offset = tty[indexTTY].screen.width *
                                tty[indexTTY].screen.heigth - 1;
        backLane();
    }

    move_csr(0);

    return 1;
}

/*Fuente: http://www.osdever.net/bkerndev/Docs/printing.htm*/
void
move_csr(int flag)
{
    int indexTTY;
    unsigned temp;

    indexTTY = CurrentTTY;

    /* The equation for finding the index in a linear
    *  chunk of memory can be represented by:
    *  Index = [(y * width) + x] */
    temp = tty[indexTTY].screen.offset;
    outportb(0x3D4, 14);
    outportb(0x3D5, temp >> 8);
    outportb(0x3D4, 15);
    outportb(0x3D5, temp);

    return;
}


void
changeColors(unsigned short bg, unsigned short txt)
{
    int indexTTY = GetTTY();
    tty[indexTTY].screen.bGColor  = bg;
    tty[indexTTY].screen.textColor= txt;

    return;
}

void
clearScreen(void)
{
    int indexTTY = GetTTY();
    tty[indexTTY].screen.offset = 0;

    memsetw((void *)tty[indexTTY].screen.address, (int)whiteSpace(),
            tty[indexTTY].screen.width * tty[indexTTY].screen.heigth);

    tty[indexTTY].screen.offset = 0;
    move_csr(0);
    return;
}

void
scroll(unsigned int n)
{
    int indexTTY = GetTTY();
    int i, j;
    unsigned char nbytes = tty[indexTTY].screen.width * 2;

    if ( n <= 0)
        return;

    for (i = 0 ; i < n ; i++)
        /* En total va de 0 a 23, (termina copiando la linea 25,
        en 24 y luego rellena 25 con ' ' */
        for (j = 0 ; j < tty[indexTTY].screen.heigth ; j++)
            memcpy((void *)tty[indexTTY].screen.address + j * nbytes,
                (const void *)tty[indexTTY].screen.address + (j+1) * nbytes, nbytes);

    /* Setea la ultima linea con espacios */
    memsetw((void *)(tty[indexTTY].screen.address +
            (tty[indexTTY].screen.heigth - 1) * tty[indexTTY].screen.width),
            (int)whiteSpace(), tty[indexTTY].screen.width);

    return;
}

unsigned short
whiteSpace(void)
{
    int indexTTY = GetTTY();
    unsigned short attrib;
    unsigned short character;

    attrib = tty[indexTTY].screen.bGColor << 4;
    attrib = attrib | tty[indexTTY].screen.textColor;
    character = ' ' | (attrib << 8);

    return character;
}

static void
showChar(unsigned short b)
{
    int indexTTY = GetTTY();
    unsigned short attrib;
    unsigned short character;

    attrib = tty[indexTTY].screen.bGColor << 4;
    attrib = attrib | tty[indexTTY].screen.textColor;
    character = b | (attrib << 8);

    tty[indexTTY].screen.address[tty[indexTTY].screen.offset] = character;
    tty[indexTTY].screen.offset++;

    return;
}

static void
breakLine(void)
{
    int indexTTY = GetTTY();
    int dephase;

    /* Calcula la distancia que falta para llegar al inicio
    de la proxima linea y se lo asigna al offset */
    dephase = tty[indexTTY].screen.width -
            (tty[indexTTY].screen.offset % tty[indexTTY].screen.width);
    tty[indexTTY].screen.offset += dephase;
    //screen.offset += PROMPT_LEN;

    return;
}

static void
backSpace(void)
{
    int indexTTY = GetTTY();
    /* Podria ocurrir que offset este en 0 y se aprete backspace :) */
    if (tty[indexTTY].screen.offset % tty[indexTTY].screen.width >  promptlen)
    {
        /* Retrocede el offset 1 lugar para insertar un ' ' */
        tty[indexTTY].screen.offset -= 1;
        videoSetByte(whiteSpace());

    /* Retrocede el offset nuevamente para poder escribir "sobre"
    el backspace simulando que lo que habia antes fue borrado */
        tty[indexTTY].screen.offset -= 1;
    }

    return;
}

static void
tabSpace(void)
{
    int indexTTY = GetTTY();
    int tabdephase;

    /* Calcula la distancia que falta para la proxima direccion
    de memoria de video modulo 8 */
    tabdephase = TAB_SIZE - (tty[indexTTY].screen.offset % TAB_SIZE);

    /* Esa diferencia se le suma luego al offset */
    tty[indexTTY].screen.offset += tabdephase;

    //if (screen.offset % VIDEO_WIDTH == 0)
      //  screen.offset += PROMPT_LEN;
    return;
}

static void
backLane(void)
{
    int indexTTY = GetTTY();
    int dephase;

    /* Calcula en cant bytes, cuanta diferencia hay entre
    la pos_x actual y pos_0 de la linea actual */
    dephase = tty[indexTTY].screen.offset % tty[indexTTY].screen.width;
    /* Le resta esa diferencia al offset, obteniendo el
    indice al principio de la linea nueva */
    tty[indexTTY].screen.offset -= dephase;
    return;
}

